这次回顾第三章的内容,这部分主要介绍了时序门,寄存器,内存以及计数器的概念。

课程官网:

https://www.nand2tetris.org/

视频地址:

https://www.coursera.org/learn/build-a-computer

Chapter 3 时序逻辑

Part 1:课程回顾

之前两章介绍的芯片都是组合芯片(combinational logic),这些芯片的特点是和时间无关。这一讲引入时序芯片(sequential logic),之所以引入时序芯片,是为了构建能够记忆“信息”的芯片,而记忆必然是之前的,所以我们首先需要开发一些表示时间的芯片。

触发器(Flip-Flop)

计算机中最基本的时序单元是触发器(Flip-Flop),其实现的功能为

本课程中我们使用名为数据触发器(data flip-flops)的变体,其架构和输入输出如下:

DFF可以由与非门(NAND)实现,方法比较复杂,老师表示本课程略过这点,所以时序芯片的基本单元为DFF,其余常用的芯片都在Project中有详细介绍。

Part 2:项目

Bit

其架构如下

假设MUX的输出为muxout,DFF有两个输出,一个为out,另一个为dffout,有了这些变量之后,按照上述架构组合即可:

CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    // Put your code here:
	DFF (in=muxout, out=out, out=dffout);
	Mux (a=dffout , b=in, sel=load, out=muxout);
}

参考资料:传送门

Register

接口如下

对每一位使用Bit芯片即可。

CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
    // Put your code here:
	Bit (in=in[0], load=load, out=out[0]);
	Bit (in=in[1], load=load, out=out[1]);
	Bit (in=in[2], load=load, out=out[2]);
	Bit (in=in[3], load=load, out=out[3]);
	Bit (in=in[4], load=load, out=out[4]);
	Bit (in=in[5], load=load, out=out[5]);
	Bit (in=in[6], load=load, out=out[6]);
	Bit (in=in[7], load=load, out=out[7]);
	Bit (in=in[8], load=load, out=out[8]);
	Bit (in=in[9], load=load, out=out[9]);
	Bit (in=in[10], load=load, out=out[10]);
	Bit (in=in[11], load=load, out=out[11]);
	Bit (in=in[12], load=load, out=out[12]);
	Bit (in=in[13], load=load, out=out[13]);
	Bit (in=in[14], load=load, out=out[14]);
	Bit (in=in[15], load=load, out=out[15]);
}

RAM8

接口如下:

第一步利用DMux8Way对每个Register设置load值,只有相应地址的Register的load值设置为输入的load;第二步根据第一步产生的load值对每个Register操作;第三步利用Mux8Way16对第二步的结果进行选择:

CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];

    PARTS:
    // Put your code here:
	DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address);
	Register (in=in, load=a, out=o0);
	Register (in=in, load=b, out=o1);
	Register (in=in, load=c, out=o2);
	Register (in=in, load=d, out=o3);
	Register (in=in, load=e, out=o4);
	Register (in=in, load=f, out=o5);
	Register (in=in, load=g, out=o6);
	Register (in=in, load=h, out=o7);
	
	Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address ,out=out);
}

RAM64

将address分为两部分,第一部分查看属于哪个RAM8,第二部分查看属于哪个Register:

CHIP RAM64 {
    IN in[16], load, address[6];
    OUT out[16];

    PARTS:
    // Put your code here:
	DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[3..5]);
	RAM8 (in=in, load=a, address=address[0..2], out=o0);
	RAM8 (in=in, load=b, address=address[0..2], out=o1);
	RAM8 (in=in, load=c, address=address[0..2], out=o2);
	RAM8 (in=in, load=d, address=address[0..2], out=o3);
	RAM8 (in=in, load=e, address=address[0..2], out=o4);
	RAM8 (in=in, load=f, address=address[0..2], out=o5);
	RAM8 (in=in, load=g, address=address[0..2], out=o6);
	RAM8 (in=in, load=h, address=address[0..2], out=o7);
	
	Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[3..5], out=out);
}

RAM512,RAM4K,RAM16K和RAM64的思路类似。

RAM512

CHIP RAM512 {
    IN in[16], load, address[9];
    OUT out[16];

    PARTS:
    // Put your code here:
	DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[6..8]);
	
	RAM64 (in=in, load=a, address=address[0..5], out=o0);
	RAM64 (in=in, load=b, address=address[0..5], out=o1);
	RAM64 (in=in, load=c, address=address[0..5], out=o2);
	RAM64 (in=in, load=d, address=address[0..5], out=o3);
	RAM64 (in=in, load=e, address=address[0..5], out=o4);
	RAM64 (in=in, load=f, address=address[0..5], out=o5);
	RAM64 (in=in, load=g, address=address[0..5], out=o6);
	RAM64 (in=in, load=h, address=address[0..5], out=o7);
	
	Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[6..8], out=out);
}

RAM4K

CHIP RAM4K {
    IN in[16], load, address[12];
    OUT out[16];

    PARTS:
    // Put your code here:
	DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[9..11]);
	
	RAM512 (in=in, load=a, address=address[0..8], out=o0);
	RAM512 (in=in, load=b, address=address[0..8], out=o1);
	RAM512 (in=in, load=c, address=address[0..8], out=o2);
	RAM512 (in=in, load=d, address=address[0..8], out=o3);
	RAM512 (in=in, load=e, address=address[0..8], out=o4);
	RAM512 (in=in, load=f, address=address[0..8], out=o5);
	RAM512 (in=in, load=g, address=address[0..8], out=o6);
	RAM512 (in=in, load=h, address=address[0..8], out=o7);
	
	Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[9..11], out=out);
}

RAM16K

注意这里使用DMux4Way:

CHIP RAM16K {
    IN in[16], load, address[14];
    OUT out[16];

    PARTS:
    // Put your code here:
	DMux4Way (in=load, a=a, b=b, c=c, d=d, sel=address[12..13]);
	
	RAM4K (in=in, load=a, address=address[0..11], out=o0);
	RAM4K (in=in, load=b, address=address[0..11], out=o1);
	RAM4K (in=in, load=c, address=address[0..11], out=o2);
	RAM4K (in=in, load=d, address=address[0..11], out=o3);
	
	Mux4Way16(a=o0 ,b=o1 ,c=o2 ,d=o3, sel=address[12..13], out=out);
}

PC

接口如下:

这部分比较难,参考了课程讨论区:传送门

社区助教给出如下架构的提示:

Register存储上一次的值,只有当reset[t],load[t],inc[t]同时为false时,输出才为out[t],所以输入Register的load为

Register的输入为o2,输出有两个,一个为out,一个为Inc16的输入;Inc16的输出为Incout。Incout和in传入Mux16,根据load做选择,得到结果o1,最后将o1和false传入Mux16,根据reset做选择,得到输出o2。完整代码如下:

CHIP PC {
    IN in[16],load,inc,reset;
    OUT out[16];

    PARTS:
    // Put your code here:
	//计算Register的load
	Or (a=reset, b=load, out=r1);
	Or (a=r1, b=inc, out=Regload);
	
	//根据load选择
	Mux16 (a=Incout, b=in, sel=load, out=o1);
	//根据reset选择
	Mux16 (a=o1, b=false, sel=reset, out=o2);
	//根据Regload选择
	Register (in=o2, load=Regload, out=out, out=o3);
	//加一
	Inc16 (in=o3, out=Incout);
}